﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Inference
{
	public static class CheckIf
	{
		/// <summary>
		/// Проверить есть ли слот с заданным именем у выбранного фрейма или у его потомков(recursive = true) 
		/// </summary>
		/// <param name="slotName"></param>
		/// <param name="recursive"></param>
		/// <returns></returns>
		public static bool SlotNameIsUnique(string slotName, string frameName, bool recursive, TaleEntities tale)
		{
			List<Slot> res = new List<Slot>();
			var ss = tale.Slot.Where(s => s.Name == slotName && s.Frame.Name == frameName);
			if (ss.Count() > 0)
				return false;
			if (recursive)
			{
				var children = from fCurrent in tale.Frame
							   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
							   where fCurrent.Name == frameName
							   select fAnother;
				foreach (Frame f in children)
				{
					if (!SlotNameIsUnique(slotName, f.Name, true, tale))
						return false;
				}
			}
			return true;
		}

		public static bool SlotIsInherited(string slotName, string frameName)
		{
			TaleEntities tale = new TaleEntities();

			var slots = from fCurrent in tale.Frame
						join fAnother in tale.Frame on fCurrent.Is_a equals fAnother.ID
						join s in tale.Slot on fAnother.ID equals s.FrameID
						where fCurrent.Name == frameName && s.Name == slotName
						select s;
			if (slots.Count() == 0)
			{
				return false;
			}
			return true;
		}

		/// <summary>
		/// Вытащить слоты с определенным именем у определенного фрейма и всех его потомков
		/// </summary>
		/// <param name="slotName">Искомое имя слота</param>
		/// <param name="frameName">Имя фрейма, в котором начинается поиск</param>
		/// <param name="tale"></param>
		/// <returns>Список слотов с указанным именем</returns>
		public static List<Slot> SlotsNamedLike(string slotName, string frameName, TaleEntities tale)
		{
			List<Slot> res = new List<Slot>();
			var ss = tale.Slot.Where(s => s.Name == slotName && s.Frame.Name == frameName);
			foreach (Slot s in ss)
			{
				res.Add(s);
			}
			var children = from fCurrent in tale.Frame
						   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
						   where fCurrent.Name == frameName
						   select fAnother;
			foreach (Frame f in children)
			{
				res.AddRange(SlotsNamedLike(slotName, f.Name, tale));
			}
			return res;
		}

		/// <summary>
		/// Получить потомков фрейма
		/// </summary>
		/// <param name="frameName">Имя фрейма</param>
		/// <param name="recursive">Если true, то в результате получим непосредственных потомков, их потомков, потомков их потомков и т.д.</param>
		/// <param name="tale"></param>
		/// <returns>Список фреймов-потомков</returns>
		public static List<Frame> ChildrenOf(string frameName, bool recursive, TaleEntities tale)
		{
			List<Frame> res = new List<Frame>();
			var children = from fCurrent in tale.Frame
						   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
						   where fCurrent.Name == frameName
						   select fAnother;
			foreach (Frame f in children)
			{
				res.Add(f);
				if (recursive)
					res.AddRange(ChildrenOf(f.Name, true, tale));
			}
			return res;
		}

		/// <summary>
		/// Достать все фреймы фреймового домена по имени корневого фрейма
		/// </summary>
		/// <param name="frameName"></param>
		/// <param name="tale"></param>
		/// <returns></returns>
		public static List<Frame> GetFrameDomain(string frameName, TaleEntities tale)
		{
			List<Frame> res = new List<Frame>();
			var children = from fCurrent in tale.Frame
						   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
						   where fCurrent.Name == frameName
						   select fAnother;
			foreach (Frame f in children)
			{
				res.Add(f);
				res.AddRange(GetFrameDomain(f.Name, tale));
			}

			var currentF = (from fCurrent in tale.Frame
							where fCurrent.Name == frameName
							select fCurrent).First();

			res.Add(currentF);
			return res;
		}

		/// <summary>
		/// Достать все фреймы фреймового домена по ID корневого фрейма
		/// </summary>
		/// <param name="frameName"></param>
		/// <param name="tale"></param>
		/// <returns></returns>
		public static List<Frame> GetFrameDomain(int frameID, TaleEntities tale)
		{
			List<Frame> res = new List<Frame>();
			var children = from fCurrent in tale.Frame
						   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
						   where fCurrent.ID == frameID
						   select fAnother;
            if (children.Count() > 0)
            {
                foreach (Frame f in children)
                {
                    res.AddRange(GetFrameDomain(f.ID, tale));                    
                }
                var currentF = (from fCurrent in tale.Frame
                                where fCurrent.ID == frameID
                                select fCurrent).First();
                res.Add(currentF);
            }
            else
            {
                var currentF = (from fCurrent in tale.Frame
                                where fCurrent.ID == frameID
                                select fCurrent).First();

                res.Add(currentF);
            }
			return res;
		}
		/// <summary>
		/// Можно ди удалить фрейм (принято, что фрейм нельзя удалить, если у него есть потомки или он используется как субфрейм)
		/// </summary>
		/// <param name="frameName">Имя фрейма</param>
		/// <returns></returns>
		public static bool FrameCouldBeDeleted(string frameName)
		{
			TaleEntities tale = new TaleEntities();
            // проверка по DefaultValue
            var subs = from fAnother in tale.Frame
                       join s in tale.Slot on fAnother.ID equals s.FrameID
                       join fCurrent in tale.Frame on s.DefaultValue equals fCurrent.ID
                       where fCurrent.Name == frameName && s.DomainType>0
                       select fAnother;
            if (subs.Count() > 0)
                return false;
            // проверка по DomainType
            var ubs = from fAnother in tale.Frame
                       join s in tale.Slot on fAnother.ID equals s.FrameID
                       join fCurrent in tale.Frame on s.DomainType equals fCurrent.ID
                       where fCurrent.Name == frameName && s.DomainType>0
                       select fAnother;
            if (ubs.Count() > 0)
                return false;
            // проверка по продукциям
            var bs = from fAnother in tale.DValues
                      join s in tale.Fact on fAnother.ID equals s.ValueID
                      join fCurrent in tale.DValues on s.ValueID equals fCurrent.ID
                      where fCurrent.Value == frameName
                      select fAnother;
            if (bs.Count() > 0)
                return false;

			return (ChildrenOf(frameName, false, tale).Count == 0);
		}

		/// <summary>
		/// Используется ли слот в продукциях
		/// </summary>
		/// <param name="slotName">Имя слота</param>
		/// <param name="frameName">Имя фрейма</param>
		/// <returns></returns>
		public static bool SlotIsInUse(string slotName, string frameName)
		{
			TaleEntities tale = new TaleEntities();

			var facts = from f in tale.Fact
						join s in tale.Slot on f.Place equals s.ID
						where s.Name == slotName && s.Frame.Name == frameName
						select f;
			if (facts.Count() > 0)
				return true;

			facts = from f in tale.Fact
					join s in tale.Slot on f.CompSlotID equals s.ID
					where s.Name == slotName && s.Frame.Name == frameName
					select f;
			if (facts.Count() > 0)
				return true;

			return false;
		}
		/// <summary>
		/// Можно ли создать связь IS_A
		/// </summary>
		/// <param name="childFrameName">Потенциальный потомок</param> 
		/// <param name="parentFrameName">Потенциальный родитель</param>
		/// <returns></returns>
		public static bool LinkISACouldBeCreated(string childFrameName, string parentFrameName)
		{
			TaleEntities tale = new TaleEntities();
			if (childFrameName == parentFrameName || ISADirectedCycle(childFrameName, parentFrameName, tale) || ParentIsASubrame(parentFrameName,childFrameName, tale))
				return false;
			return true;
		}

		/// <summary>
		/// является ли потенциальный субфрейм папой потенциального владельца
		/// </summary>
		/// <param name="subFrameName"></param>
		/// <param name="ownerFrameName"></param>
		/// <param name="tale"></param>
		/// <returns></returns>
		public static bool ParentIsASubrame(string subFrameName, string ownerFrameName, TaleEntities tale)
		{
			var ow = tale.Frame.Where(f => f.Name == ownerFrameName).First();
			if (ow.Is_a != null)
			{
				var par = tale.Frame.Where(f => f.ID == ow.Is_a).First();
				if (par.Name == subFrameName || SUBDirectedCycle(subFrameName, par.Name, tale))
				{
					return true;
				}
				else
					if (ParentIsASubrame(subFrameName, par.Name, tale))
						return true;
			}

			return false;
		}

		


		/// <summary>
		/// есть ли направленный цикл по IS_A
		/// </summary>
		/// <param name="childFrameName"></param>
		/// <param name="parentFrameName"></param>
		/// <param name="tale"></param>
		/// <returns></returns>
		public static bool ISADirectedCycle(string childFrameName, string parentFrameName, TaleEntities tale)
		{
			var children = from fCurrent in tale.Frame
						   join fAnother in tale.Frame on fCurrent.ID equals fAnother.Is_a
						   where fCurrent.Name == childFrameName
						   select fAnother;
			foreach (Frame f in children)
			{
				if (f.Name == parentFrameName)
					return true;
				if (ISADirectedCycle(f.Name, parentFrameName, tale))
					return true;
			}
			return false;
		}

		static string GetRoot(string frameName)
		{
			TaleEntities tale = new TaleEntities();
			Frame fr;
			fr = tale.Frame.Where(f => f.Name == frameName).First();
			int FrameID = fr.ID;
			do
			{
				fr = tale.Frame.Where(f => f.ID == FrameID).First();
				if (fr.Is_a.HasValue)
					FrameID = fr.Is_a.Value;
				else
					FrameID = 0;
			}
			while (FrameID != 0);
			return fr.Name;
		}

		/// <summary>
		/// Можно ли создать связь SUB
		/// </summary>
		/// <param name="childFrameName">Потенциальный субфрейм</param> 
		/// <param name="parentFrameName">Потенциальный владелец</param>
		/// <returns></returns>
		public static bool LinkSUBCouldBeCreated(string subFrameName, string ownerFrameName)
		{
			TaleEntities tale = new TaleEntities();
			if (subFrameName == ownerFrameName || SUBDirectedCycle(subFrameName, ownerFrameName, tale) || ParentIsASubrame(subFrameName, ownerFrameName, tale) || GetRoot(subFrameName) == "Ситуация")
				return false;
			return true;
		}
		/// <summary>
		/// есть ли направленный цикл по SUB
		/// </summary>
		/// <param name="childFrameName"></param>
		/// <param name="parentFrameName"></param>
		/// <param name="tale"></param>
		/// <returns></returns>
		public static bool SUBDirectedCycle(string subFrameName, string ownerFrameName, TaleEntities tale)
		{
			var subs = from fCurrent in tale.Frame
					   join s in tale.Slot on fCurrent.ID equals s.FrameID
					   join fAnother in tale.Frame on s.DefaultValue equals fAnother.ID
					   where fCurrent.Name == subFrameName && s.DomainType != null && s.DomainType != 0
					   select fAnother;
			foreach (Frame f in subs)
			{
				if (f.Name == ownerFrameName)
					return true;
				if (SUBDirectedCycle(f.Name, ownerFrameName, tale))
					return true;
			}
			return false;
		}

		public static bool DomainCouldBeDeleted(string domainName)
		{
			TaleEntities tale = new TaleEntities();
			var d = tale.Slot.Where(s => s.Domains.Name == domainName);
			if (d.Count() > 0)
				return false;
			return true;
		}

		public static bool DomainValueCouldBeDeleted(string domainValue, string domainName)
		{
			TaleEntities tale = new TaleEntities();
			var q = from f in tale.Fact
					join dv in tale.DValues on f.ValueID equals dv.ID
					join domval in tale.DomainsValues on dv.ID equals domval.Value
					join dom in tale.Domains on domval.DomID equals dom.ID
					where dom.Name == domainName && dv.Value == domainValue && f.Slot.Domains.Name == domainName
					select f;
			if (q.Count() > 0)
				return false;
			var doms = tale.Domains.Where(d => d.Name == domainName);
			if(doms.Count() > 0)
				if (doms.FirstOrDefault().DomainsValues.Count == 1)
					return false;
			return true;
		}


		public static bool ISACouldBeDeleted(string childFrameName)
		{
			TaleEntities tale = new TaleEntities();
			var slots = from fCurrent in tale.Frame
						join fAnother in tale.Frame on fCurrent.Is_a equals fAnother.ID
						join s in tale.Slot on fAnother.ID equals s.FrameID
						where fCurrent.Name == childFrameName
						select s;

			foreach (var s in slots)
			{
				var inheritedSlots = SlotsNamedLike(s.Name, childFrameName, tale);
				foreach (Slot sl in inheritedSlots)
				{
					if (SlotIsInUse(sl.Name, sl.Frame.Name))
						return false;
				}
			}
			return true;
		}

		public static List<Slot> GetProductionChain(int place, int compSlotId)
		{
			TaleEntities tale = new TaleEntities();
			Frame f = tale.Frame.Where(ff => ff.Slot.Where(ss => ss.ID == place).Count() > 0).First();
			var res = GetPartOfProductionChain(f, compSlotId, tale);
			int i = 0;
			int j = 0;
			if (res.Count > 0)
			{
				while (i < res.Count && res[i].ID != place)
					i++;
				while (i + j < res.Count && res[i + j].ID != compSlotId)
				{
					j++;
				}
                if (i < res.Count)
                {
                    return res.GetRange(i, j + 1);
                }
                else
                {
                    return res.GetRange(i-1, j+1);
                }
			}
			return new List<Slot>();
		}

		public static List<Slot> GetPartOfProductionChain(Frame currentFrame, int compSlotId, TaleEntities tale)
		{
			List<Slot> res = new List<Slot>();

			foreach (Slot ss in currentFrame.Slot)
			{
				if (ss.ID == compSlotId)
				{
					res.Add(ss);
					return res;
				}
			}
            //var subs = from s in currentFrame.Slot
            //           join fAnother in tale.Frame on s.DefaultValue equals fAnother.ID
            //           where s.Domain == 1 || s.Domain == 7
            //           select new
            //           {
            //               fr = fAnother,
            //               sl = s
            //           };
            // mine variant
            var sub_slots = tale.Slot.Where(v=>v.FrameID == currentFrame.ID && (v.DomainType!=null && v.DomainType>0) && v.Name.Trim().ToLower()!="next");            
			foreach (var sub in sub_slots)
			{                
                var fr = tale.Frame.Where(f=>f.ID == sub.DefaultValue).First();
				var qq = GetPartOfProductionChain(/*sub.*/fr, compSlotId, tale);
				if (qq.Count > 0)
				{
					res.Add(sub/*.sl*/);
					res.AddRange(qq);
				}
			}
			return res;
		}
	}
}
